﻿#include "CModBusOper.h"
#include <QDebug>
#include "CGlobal.h"
#include <QTimer>
#include <QEventLoop>
#include <QTimerEvent>

CModBusOper::CModBusOper(QString sIp, int nPort, int nDateType)
{

	m_pMutex = new QMutex(QMutex::Recursive);
	m_pMutexDate = new QMutex();
	m_pSysMutex = new QMutex();
	m_nDateType = nDateType;
	ResetData();
	m_sIp = sIp;
	m_nPort = nPort;
	m_bThread = true;
	m_pExecThread = new QThread();
	this->moveToThread(m_pExecThread);
	connect(m_pExecThread, SIGNAL(started()), this, SLOT(slot_ThreadWork()));
	m_pExecThread->start();
	m_bModConnect = false;
	m_sReError = "";
	m_pWaitCondition = new QWaitCondition();
	m_nSlaveId = 1;
	m_nDisConnect = 0;
	m_bConnect = false;
	m_bGood = false;
}

void CModBusOper::setSlaveId(int nId)
{
	m_nSlaveId = nId;
}

CModBusOper::~CModBusOper()
{
	m_bThread = false;
	m_pExecThread->quit();
	if (!m_pExecThread->wait(5000))
	{
		m_pExecThread->terminate();
	}
	if (m_pExecThread != nullptr)
	{
		delete m_pExecThread;
		m_pExecThread = nullptr;
	}

	m_pModTcp->disconnect();
	if (m_pModTcp != nullptr)
	{
		delete m_pModTcp;
		m_pModTcp = nullptr;
	}

	if (m_pMutex != nullptr)
	{
		delete m_pMutex;
		m_pMutex = nullptr;
	}

	if (m_pMutexDate != nullptr)
	{
		delete m_pMutexDate;
		m_pMutexDate = nullptr;
	}

	if (m_pSysMutex != nullptr)
	{
		delete m_pSysMutex;
		m_pSysMutex = nullptr;
	}

	if (m_pWaitCondition != nullptr)
	{
		delete m_pWaitCondition;
		m_pWaitCondition = nullptr;
	}
}

void CModBusOper::InitMod()
{
	QVariant vIp = m_sIp;
	QVariant vPort = m_nPort;
	m_pModTcp->setConnectionParameter(QModbusDevice::NetworkPortParameter, vPort);
	m_pModTcp->setConnectionParameter(QModbusDevice::NetworkAddressParameter, vIp);
	m_pModTcp->setTimeout(3000);
	m_pModTcp->setNumberOfRetries(m_nDateType);
}

bool CModBusOper::ReadValsOperEx(int nAddr, int nLength)
{
	try
	{
		for (int i = 0; i < 3; i++)
		{
			if (ReadValsOper(nAddr, nLength))
			{
				return true;
			}
		}
		if (m_nDisConnect > 15)
		{
			m_nDisConnect = 0;
		}
		m_nDisConnect++;
		return false;
	}
	catch (const std::exception&)
	{
		return false;
	}


}

bool CModBusOper::WriteValsOperEx(int nAddr, int nLength, QVector<int>vecVal)
{
	try
	{
		for (int i = 0; i < 3; i++)
		{
			if (WriteValsOper(nAddr, nLength, vecVal))
			{
				return true;
			}
		}
		if (m_nDisConnect > 15)
		{
			m_nDisConnect = 0;
		}
		m_nDisConnect++;
		return false;
	}
	catch (const std::exception&)
	{
		return false;
	}

}


bool CModBusOper::ReadVal(int nAddr, int & nReVal)
{
	if (m_pMutex == nullptr)
	{
		return false;
	}
	if (m_pMutex->tryLock(3000))
	{
		if (ReadValsOperEx(nAddr, 1))
		{
			QVector<int>vecVal;
			vecVal = GetReadVal();
			if (vecVal.size() > 0)
				nReVal = vecVal[0];
			else
				nReVal = -1;
			m_pMutex->unlock();
			return true;
		}
		else
		{
			m_pMutex->unlock();
			return false;
		}
	}
	else
	{
		return false;
	}
}

bool CModBusOper::ReadVal(int nAddr, int nLength, int & nReVal)
{
	if (m_pMutex == nullptr)
	{
		return false;
	}
	if (m_pMutex->tryLock(3000))
	{
		if (ReadValsOperEx(nAddr, 1))
		{
			nReVal = 0;
			QVector<int>vecVal;
			vecVal = GetReadVal();
			if (vecVal.size() > 0)
			{
				for (int i = 0; i < vecVal.size(); i++)
				{
					nReVal = nReVal * 65536 + vecVal[i];
				}
			}
			else
			{
				nReVal = -1;
			}
			m_pMutex->unlock();
			return true;
		}
		else
		{
			m_pMutex->unlock();
			return false;
		}
	}
	else
	{
		return false;
	}
}



bool CModBusOper::ReadValEx(int nAddr, int nLength, QVector<int>& vecReVal)
{
	if (m_pMutex == nullptr)
	{
		return false;
	}
	if (m_pMutex->tryLock(3000))
	{
		int j = 0;
		vecReVal.clear();
		for (int i = 0; i < nLength; i++)
		{
			if (i > 0 && ((i + 1) % 100 == 0))
			{
				if (!ReadValsOperEx(nAddr + j * 100, 100))
				{
					m_pMutex->unlock();
					return false;
				}
				else
				{
					QVector<int>vecVal;
					vecVal = GetReadVal();
					if (vecVal.size() == 100)
					{
						for (int i = 0; i < vecVal.size(); i++)
						{
							vecReVal.push_back(vecVal[i]);
						}
					}
					else
					{
						m_pMutex->unlock();
						return false;
					}
				}
				j++;
			}
			else if (i == nLength - 1)
			{
				if (!ReadValsOperEx(nAddr + j * 100, nLength % 100))
				{
					m_pMutex->unlock();
					return false;
				}
				else
				{
					if (m_vecReadVal.size() == nLength % 100)
					{
						for (int i = 0; i < m_vecReadVal.size(); i++)
						{
							vecReVal.push_back(m_vecReadVal[i]);
						}
					}
					else
					{
						m_pMutex->unlock();
						return false;
					}
				}
				j++;
			}
		}
		m_pMutex->unlock();
		return true;
	}
	else
	{
		return false;
	}
}

bool CModBusOper::WriteVal(int nAddr, int nVal)
{
	if (m_pMutex == nullptr)
	{
		return false;
	}
	if (m_pMutex->tryLock(3000))
	{
		QVector<int>vecVal;
		vecVal.push_back(nVal);
		if (WriteValsOperEx(nAddr, 1, vecVal))
		{
			m_pMutex->unlock();
			return true;
		}
		else
		{
			m_pMutex->unlock();
			return false;
		}
	}
	else
	{
		return false;
	}
}

bool CModBusOper::WriteValCoil(int nAddr, int)
{
	return false;
}

bool CModBusOper::WriteVal(int nAddr, int nLength, int nVal)
{
	if (m_pMutex == nullptr)
	{
		return false;
	}
	if (m_pMutex->tryLock(3000))
	{
		QVector<int> vecVal;
		vecVal.resize(nLength);
		for (int i = nLength - 1; i >= 0; i--)
		{
			vecVal[i] = nVal % 65536;
			nVal = (nVal / 65536);
		}
		if (WriteValsOperEx(nAddr, nLength, vecVal))
		{
			m_pMutex->unlock();
			return true;
		}
		else
		{
			m_pMutex->unlock();
			return false;
		}
	}
	else
	{
		return false;
	}
}

bool CModBusOper::WriteValEx(int nAddr, int nLength, QVector<int>vecVal)
{
	if (m_pMutex == nullptr)
	{
		return false;
	}
	if (m_pMutex->tryLock(3000))
	{
		QVector<int>vecValEx;
		int j = 0;
		for (int i = 0; i < nLength; i++)
		{
			vecValEx.push_back(vecVal[i]);
			if (i > 0 && ((i + 1) % 100 == 0))
			{
				if (!WriteValsOperEx(nAddr + j * 100, 100, vecValEx))
				{
					m_pMutex->unlock();
					return false;
				}
				j++;
				vecValEx.clear();
			}
			else if (i == nLength - 1)
			{
				if (!WriteValsOperEx(nAddr + j * 100, nLength % 100, vecValEx))
				{
					m_pMutex->unlock();
					return false;
				}
			}
		}
		m_pMutex->unlock();
		return true;
	}
	else
	{
		return false;
	}
}

QString CModBusOper::GerReError()
{
	QMutexLocker locker(m_pMutexDate);
	return m_sReError;
}

bool CModBusOper::ReadValsOper(int nAddr, int nLength)
{
	m_nAddr = nAddr;
	m_nLength = nLength;
	m_nThreadCmd = 1;
	if (WaitSys(1000))
	{
		if (nLength == m_vecReadVal.size())
		{
			m_nDisConnect = 0;
			return true;
		}
		else
		{
			if (m_nDisConnect > 15)
			{
				m_bConnect = false;
				m_nDisConnect = 0;
			}
			m_nDisConnect++;
			m_bModConnect = false;
			return false;
		}
	}
	else
	{
		//记录日志
		/*SExecLog sLog;
		sLog.m_dLogTime = QDateTime::currentDateTime();
		sLog.m_nAlarmLevel = enProcessAlarm;
		sLog.m_sDescriptionl = A2T("Modbus等待超时P:%1").arg(m_sIp);
		sLog.m_bExecResult = false;
		sLog.m_sDevName = A2T("PLC");
		CLogOper::instance().addMsg("", sLog);
		m_bModConnect = false;
		m_nThreadCmd = 99;

		if (m_nDisConnect > 15)
		{
			m_bConnect = false;
			m_nDisConnect = 0;
		}*/
		m_nDisConnect++;
		return false;
	}
}

bool CModBusOper::WriteValsOper(int nAddr, int nLength, QVector<int>vecVal)
{
	if (nLength != vecVal.size())
	{
		return false;
	}
	m_nAddr = nAddr;
	m_nLength = nLength;
	SetWriteVal(vecVal);
	m_nThreadCmd = 2;
	if (WaitSys(600))
	{
		if (m_bGood)
		{
			m_nDisConnect = 0;
			return true;
		}
		else
		{
			if (m_nDisConnect > 15)
			{
				m_bConnect = false;
				m_nDisConnect = 0;
			}
			m_nDisConnect++;
			return false;
		}
	}
	else
	{
		m_nThreadCmd = 99;
		if (m_nDisConnect > 15)
		{
			m_bConnect = false;
			m_nDisConnect = 0;
		}
		m_nDisConnect++;
		return false;
	}
}


void CModBusOper::SleepSysMs(int nMs)
{
	int nCount = 0;
	if (nMs > 5)
	{
		nCount = nMs / 5;
		for (int i = 0; i < nCount; i++)
		{
			QEventLoop loop;
			QTimer::singleShot(5, &loop, SLOT(quit()));
			loop.exec();
			if (m_nThreadCmd == 3)
			{
				break;
			}
		}
	}
	else
	{
		QEventLoop loop;
		QTimer::singleShot(nMs, &loop, SLOT(quit()));
		loop.exec();
	}
}

void CModBusOper::ThreadReadVal()
{
	if (m_bConnect == false)
	{
		m_nThreadCmd = 3;
		m_pModTcp->disconnect();
		m_bConnect = true;
		m_bGood = false;
		return;
	}
	m_pMutexDate->lock();
	m_vecReadVal.clear();
	m_vecReadVal.shrink_to_fit();
	m_pMutexDate->unlock();
	QList<QModbusReply *> pendingReplies = m_pModTcp->findChildren<QModbusReply *>();
	for (QModbusReply *replyEx : pendingReplies)
	{
		if (replyEx)
			replyEx->deleteLater(); // 清理未处理的回复
	}
	QModbusDataUnit ReadUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters, m_nAddr - 40001, m_nLength);  //设置寄存器类型、地址、寄存器个数;
	if (auto *reply = m_pModTcp->sendReadRequest(ReadUnit, m_nSlaveId)) //1表示modbus从站地址，在本系统中，只有一个从站，地址为1
	{
		if (!reply->isFinished())
		{
			QEventLoop loop;
			QTimer timeoutTimer;
			timeoutTimer.setSingleShot(true);
			connect(&timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit);
			connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
			timeoutTimer.start(600);
			loop.exec();
			if (timeoutTimer.isActive())
			{
				timeoutTimer.stop();
				slot_ReadVal(reply);

			}
			else
			{
				delete reply;
				QString sReError = A2T("读取数据超时");
				SetReError(sReError);
				m_nThreadCmd = 3;
			}


		}
		else
		{
			delete reply;
			QString sReError = A2T("Modbus发送读取数据失败");
			SetReError(sReError);
			m_nThreadCmd = 3;
		}
	}
	else
	{
		QString sReError = "ModbusRead error: " + m_pModTcp->errorString();
		SetReError(sReError);
		m_nThreadCmd = 3;
	}

}

void CModBusOper::ThreadWriteVal()
{
	if (m_bConnect == false)
	{
		m_nThreadCmd = 3;
		m_pModTcp->disconnect();
		m_bConnect = true;
		return;
	}

	m_pMutexDate->lock();
	QList<QModbusReply *> pendingReplies = m_pModTcp->findChildren<QModbusReply *>();
	for (QModbusReply *replyEx : pendingReplies)
	{
		if (replyEx)
			replyEx->deleteLater(); // 清理未处理的回复
	}
	QModbusDataUnit WriteUnit(QModbusDataUnit::HoldingRegisters, m_nAddr - 40001, m_nLength);//设置寄存器类型、地址、寄存器个数
	for (quint16 i = 0; i < m_nLength; i++)
	{
		WriteUnit.setValue(i, m_vecWriteVal[i]);
	}
	m_pMutexDate->unlock();
	if (auto *reply = m_pModTcp->sendWriteRequest(WriteUnit, m_nSlaveId))
	{
		if (!reply->isFinished())
		{
			QEventLoop loop;
			QTimer timeoutTimer;
			timeoutTimer.setSingleShot(true);
			connect(&timeoutTimer, &QTimer::timeout, &loop, &QEventLoop::quit);
			connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
			timeoutTimer.start(600);
			loop.exec();
			if (timeoutTimer.isActive())
			{
				timeoutTimer.stop();
				slot_WriteVal(reply);
				m_nThreadCmd = 3;

			}
			else
			{
				delete reply;
				QString sReError = A2T("Modbus发送写数据超时");
				SetReError(sReError);
				m_nThreadCmd = 3;
			}
		}
		else
		{
			delete reply;
			QString sReError = A2T("Modbus发送写数据失败");
			SetReError(sReError);
			m_nThreadCmd = 3;
		}
	}
	else
	{

		QString sReError = "ModbusRead error: " + m_pModTcp->errorString();
		SetReError(sReError);
		m_nThreadCmd = 3;
	}
}

void CModBusOper::ThreadReadValEx()
{
	if (m_pModTcp->state() != QModbusDevice::ConnectedState)
	{
		m_nThreadCmd = 3;
		return;
		/*if (!ConnectMod())
		{
			m_nModState = 3;
			m_nThreadCmd = 3;
			return;
		}*/
	}

	m_pMutexDate->lock();
	QModbusDataUnit WriteUnit(QModbusDataUnit::Coils, m_nAddr, m_nLength);//设置寄存器类型、地址、寄存器个数
	for (quint16 i = 0; i < m_nLength; i++)
	{
		WriteUnit.setValue(i, m_vecWriteVal[i]);
	}
	m_pMutexDate->unlock();
	if (auto *reply = m_pModTcp->sendWriteRequest(WriteUnit, m_nSlaveId))
	{
		if (!reply->isFinished())
		{
			//connect(reply, &QModbusReply::finished, this, &CModBusOper::slot_WriteVal);
		}
		else
		{
			delete reply;
			QString sReError = A2T("Modbus发送写数据失败");
			SetReError(sReError);
			m_nThreadCmd = 3;
		}
	}
	else
	{

		QString sReError = "ModbusRead error: " + m_pModTcp->errorString();
		SetReError(sReError);
		m_nThreadCmd = 3;
	}
}

void CModBusOper::ThreadWriteValEx()
{
	if (m_pModTcp->state() != QModbusDevice::ConnectedState)
	{
		m_nThreadCmd = 3;
		return;
		/*if (!ConnectMod())
		{
			m_nModState = 3;
			m_nThreadCmd = 3;
			return;
		}*/
	}

	m_pMutexDate->lock();
	QModbusDataUnit WriteUnit(QModbusDataUnit::Coils, m_nAddr, m_nLength);//设置寄存器类型、地址、寄存器个数
	for (quint16 i = 0; i < m_nLength; i++)
	{
		WriteUnit.setValue(i, m_vecWriteVal[i]);
	}
	m_pMutexDate->unlock();
	if (auto *reply = m_pModTcp->sendWriteRequest(WriteUnit, m_nSlaveId))
	{
		if (!reply->isFinished())
		{
			//connect(reply, &QModbusReply::finished, this, &CModBusOper::slot_WriteVal);
		}
		else
		{
			delete reply;
			QString sReError = A2T("Modbus发送写数据失败");
			SetReError(sReError);
			m_nThreadCmd = 3;
		}
	}
	else
	{

		QString sReError = "ModbusRead error: " + m_pModTcp->errorString();
		SetReError(sReError);
		m_nThreadCmd = 3;
	}
}

void CModBusOper::ResetData()
{
	m_nThreadCmd = 0;
	m_nLength = 0;
	m_nAddr = 0;
	SetReError("");
	m_bGood = false;
	m_vecWriteVal.clear();
	m_vecWriteVal.shrink_to_fit();
}

QVector<int> CModBusOper::GetReadVal()
{
	QVector<int>vecVal;
	QMutexLocker cLocker(m_pMutexDate);
	vecVal = m_vecReadVal;
	m_vecReadVal.clear();
	return vecVal;
}

void CModBusOper::SetWriteVal(QVector<int> vecVal)
{
	QMutexLocker cLocker(m_pMutexDate);
	m_vecWriteVal = vecVal;
}

void CModBusOper::SetReError(QString sReError)
{
	QMutexLocker locker(m_pMutexDate);
	m_sReError = sReError;
}

void CModBusOper::timerEvent(QTimerEvent * event)
{
	if (event->timerId() == m_nConnectTimer)
	{
		if (m_pModTcp->state() != QModbusDevice::ConnectedState)
		{
			//记录日志
			/*SExecLog sLog;
			sLog.m_dLogTime = QDateTime::currentDateTime();
			sLog.m_nAlarmLevel = enProcessAlarm;
			sLog.m_sDescriptionl = A2T("PLC离线，开始执行重连，Modbus状态:%1,::%2").arg(m_pModTcp->state()).arg(m_sIp);
			sLog.m_bExecResult = false;
			sLog.m_sDevName = A2T("PLC");
			CLogOper::instance().addMsg("", sLog);
			if (m_pModTcp->connectDevice())
			{
				m_bConnect = true;
			}*/
		}
	}
}



void CModBusOper::slot_ReadVal(QModbusReply *reply)
{
	if (QModbusDevice::NoError == reply->error())
	{
		QModbusDataUnit unit = reply->result();
		QMutexLocker locker(m_pMutexDate);
		for (uint i = 0; i < unit.valueCount(); i++)
		{
			m_vecReadVal.push_back(unit.value(i));
		}
	}
	else
	{
		SetReError(reply->errorString());
	}
	reply->deleteLater();
	m_nThreadCmd = 3;
}

bool CModBusOper::WaitSys(int nSystime)
{
	m_pSysMutex->lock();
	if (m_pWaitCondition->wait(m_pSysMutex, nSystime))
	{
		m_pSysMutex->unlock();
		return true;
	}
	else
	{
		m_pSysMutex->unlock();
		return false;
	}
}

void CModBusOper::slot_WriteVal(QModbusReply *reply)
{
	if (QModbusDevice::NoError == reply->error())
	{
		m_bGood = true;
	}
	else
	{
		SetReError(reply->errorString());
		m_bGood = false;
	}
	reply->deleteLater();
	m_nThreadCmd = 3;
}

void CModBusOper::onStateChanged(int nState)
{
	/*bool connected = (nState != QModbusDevice::UnconnectedState);
	m_bModConnect = connected;
	if (!m_bModConnect)
	{
		SleepSysMs(5);
		ConnectMod();
	}*/
}

void CModBusOper::slot_ThreadWork()
{
	m_pModTcp = new QModbusTcpClient(this);
	InitMod();
	m_pModTcp->connectDevice();
	m_nConnectTimer = startTimer(5000);
	while (m_bThread)
	{
		if (!m_bThread)
		{
			killTimer(m_nConnectTimer);
		}
		switch (m_nThreadCmd)
		{
		case 1:
			if (m_nDateType == 3)
			{
				ThreadReadVal();
			}
			else
			{
				ThreadReadValEx();
			}

			break;
		case 2:
			if (m_nDateType == 3)
			{
				ThreadWriteVal();
			}
			else
			{
				ThreadWriteValEx();
			}

			break;
		case 3:
			m_nThreadCmd = 0;
			m_pWaitCondition->wakeAll();
			break;
		}

		SleepSysMs(5);
	}
}


